/**
 * 2020:created by jordia
 * 20220304:[require] to [import]...jordia
 * 20221018: restructuring with typescript, major alteration.
 */
 import {MathUtils} from 'three';


 /**
  * Interrupter
  * usage
  * 1.After instantiation, specify the value (get) and value (set) functions
  * 2.Specify the value that should be assigned when entering the interrupt state (inValue)
  * 3.(Optional) Specifies the value that should be assigned when exiting the interrupt state when forced assignment is enabled (outValue)
  * 4.(Optional) Set whether to use the record value or exit value when exiting the interrupt status (enforce)
  * 5.interrupt：true ,Enter the interrupt state and record the current value_ OldValue and set (inValue)
  * 6.interrupt：false ,Exit interrupt status, set (oldValue) or set (outValue)
  *
  * Examples of interruptions for dragging components and camera components:
  *      let br=new Breaker();
  *      br.get=()=>{
  *                camCtr.releaseUnfinishedEvent();
  *                return camCtr.enabled;
  *          };                                      //Method to record the status of related components (release the camera control 
  *                                                    suspend event and return whether the camera control is currently available)
  *      br.set=(value)=>{
  *                camCtr.releaseUnfinishedEvent();
  *                camCtr.enabled=value;
  *          };                                      //Method of assigning values to related components (release camera control suspend 
  *                                                    event and enable/disable camera control)
  *      br.inValue=false;                           //Disable camera control when interrupting entry (inValue=false)
  *      drag.addBreaker(br);                        //Add interrupter to drag component
  *      br.outValue=true;                           //Set to enable camera control when interrupt exits (if enforce=true, otherwise use
  *                                                    the recorded value)
  *      br.enforce=true;                            //Interrupt Exit does not use recorded values, but forces camera control to be 
  *                                                    enabled (because outValue=true)
  *
  *      When pointerMove in the corresponding drag component：
  *          br.interrupt=true;
  *      When pointerOut in the corresponding drag component：
  *          br.interrupt=false;
  */
 export default class Breaker<T>{
    private _getCall:{():T}|null=null;
    private _oldValue:T|null=null;
    private _setCall:{(value:T):void}|null=null;
    private _breakValue:T|null=null;
    private _enforce:boolean=false;
    private _continueValue:T|null=null;

    public readonly type='Breaker';
    public readonly uuid=MathUtils.generateUUID();
 
     /**
      * Enter/exit interrupt state
      */
     public set interrupt(bol:boolean){
         if(bol){
             if(this._getCall)this._oldValue=this._getCall();
             if(this._setCall)this._setCall(this._breakValue!);
         }else{
             if(!this._setCall)return;
             if(!this._enforce){
                 this._setCall(this._oldValue!);
                 return;
             }
             this._setCall(this._continueValue!);
         }
     }
 
     /**
      * set value getter
      */
     public set get(func_return_value:{():T}){
         this._getCall=func_return_value;
     }
 
     /**
      * set value setter
      */
     public set set(func_paras_value:{(value:T):void}){
         this._setCall=func_paras_value;
     }
 
     /**
      * value to enter break state
      */
     public set inValue(value:T){
         this._breakValue=value;
     }
 
     /**
      * value to exit break state (used for mandatory assignment)
      */
     public set outValue(value:T){
         this._continueValue=value;
     }
 
     /**
      * Forced assignment when exiting interrupt status
      */
     public set enforce(bol:boolean){
         if(bol){
             this._enforce=true;
             return;
         }
         this._enforce=false;
     }
 
     /**
      * destroy
      */
     public dispose():void{
         this._getCall=null;
         this._setCall=null;
         this._breakValue=null;
         this._oldValue=null;
         this._continueValue=null;
         this._enforce=false;
     }
 
 }